package com.lagou.edu.proxy;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Component;
import com.lagou.edu.jdbc.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;

/**
 * @author fanbo@imoran.net
 * @date 2020/12/26 下午2:48
 */
@Component(value = "proxyFactory")
public class ProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    /**
     * Jdk动态代理
     *
     * @param obj                委托对象
     * @param interfaces         接口集合
     * @param transactionMethods 需要开启事务的方法
     * @return 代理对象
     */
    public Object getJdkProxy(Object obj, Class<?>[] interfaces, List<Method> transactionMethods) {

        /** 获取代理对象 */
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(), interfaces,
                (proxy, method, args) -> {

                    /** 是否需要使用事务 */
                    Boolean needTransaction = false;

                    String methodName = method.getName();
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    for (Method transactionMethod : transactionMethods) {
                        /** 方法名相同 && 参数相同 */
                        if (transactionMethod.getName().equals(methodName) &&
                                Arrays.equals(transactionMethod.getParameterTypes(), parameterTypes)) {
                            needTransaction = true;
                        }
                    }

                    Object result = null;
                    /** 如果是标注了@Transaction的方法，开启事务 */
                    if (needTransaction) {
                        try {
                            /** 开启事务(关闭事务的自动提交) */
                            transactionManager.beginTransaction();

                            result = method.invoke(obj, args);

                            /** 提交事务 */
                            transactionManager.commit();
                        } catch (InvocationTargetException e) {
                            /** 回滚事务 */
                            transactionManager.rollback();
                            e.printStackTrace();

                            /** 抛出异常便于上层servlet捕获 */
                            Throwable trowEx = e.getTargetException();
                            throw new RuntimeException(trowEx.getMessage());
                        }
                    } else {
                        result = method.invoke(obj, args);
                    }


                    return result;
                });

    }


    /**
     * 使用cglib动态代理生成代理对象
     *
     * @param obj                委托对象
     * @param transactionMethods 需要开启事务的方法
     * @return
     */
    public Object getCglibProxy(Object obj, List<Method> transactionMethods) {
        return Enhancer.create(obj.getClass(), (MethodInterceptor) (o, method, objects, methodProxy) -> {
            Object result = null;

            /** 是否需要使用事务 */
            Boolean needTransaction = false;

            String methodName = method.getName();
            Class<?>[] parameterTypes = method.getParameterTypes();
            for (Method transactionMethod : transactionMethods) {
                /** 方法名相同 && 参数相同 */
                if (transactionMethod.getName().equals(methodName) &&
                        Arrays.equals(transactionMethod.getParameterTypes(), parameterTypes)) {
                    needTransaction = true;
                }
            }

            if (needTransaction) {
                try {
                    /** 开启事务(关闭事务的自动提交) */
                    transactionManager.beginTransaction();

                    result = method.invoke(obj, objects);

                    /** 提交事务 */
                    transactionManager.commit();
                } catch (InvocationTargetException e) {
                    /** 回滚事务*/
                    transactionManager.rollback();
                    e.printStackTrace();

                    /** 抛出异常便于上层servlet捕获 */
                    Throwable trowEx = e.getTargetException();
                    throw new RuntimeException(trowEx.getMessage());
                }

            } else {
                result = method.invoke(obj, objects);
            }

            return result;
        });
    }
}
